Go 连接 MongoDB
初始化环境
启动服务
mongod \
--dbpath /var/lib/mongo \
--logpath /var/log/mongodb/mongod.log \
--fork
打开客户端
$ mongo
初始化仓库
// 创建数据库
use go_db;
// 创建集合
db.createCollection("student");
下载驱动
go get github.com/mongodb/mongo-go-driver
连接数据库
package main
import (
"context"
"fmt"
"log"
"time"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func main() {
var (
client *mongo.Client
err error
db *mongo.Database
collection *mongo.Collection
)
//1.建立连接
if client, err = mongo.Connect(context.TODO(),
options.Client().ApplyURI("mongodb://localhost:27017").
SetConnectTimeout(5*time.Second)); err != nil {
fmt.Print(err)
return
}
// 2.检查连接
err = client.Ping(context.TODO(), nil)
if err != nil {
log.Fatal(err)
}
fmt.Println("Connected to MongoDB!")
//3.选择数据库 my_db
db = client.Database("my_db")
//4.选择表 my_collection
collection = db.Collection("my_collection")
collection = collection
}
将连接函数提取出来
创建 util 包
import (
"context"
"log"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
var mgoCli *mongo.Client
func initEngine() {
var err error
clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")
// 连接到 MongoDB
mgoCli, err = mongo.Connect(context.TODO(), clientOptions)
if err != nil {
log.Fatal(err)
}
// 检查连接
err = mgoCli.Ping(context.TODO(), nil)
if err != nil {
log.Fatal(err)
}
}
func GetMgoCli() *mongo.Client {
if mgoCli == nil {
initEngine()
}
return mgoCli
}
插入一条数据
构建几个结构体
type TimePoint struct {
StartTime int64 `bson:"startTime"` //开始时间
EndTime int64 `bson:"endTime"` //结束时间
}
type LogRecord struct {
JobName string `bson:"jobName"` //任务名
Command string `bson:"command"` //shell命令
Err string `bson:"err"` //脚本错误
Content string `bson:"content"` //脚本输出
Tp TimePoint //执行时间
}
关于这个 BSON 的 Tag 文档参考 Work with BSON
main 函数:
func main() {
var (
client = util.GetMgoCli()
err error
collection *mongo.Collection
iResult *mongo.InsertOneResult
id primitive.ObjectID
)
//2.选择数据库 my_db里的某个表
collection = client.Database("my_db").Collection("my_collection")
lr := &model.LogRecord{
JobName: "PlanA",
Command: "echo 'say hello'",
Err: "",
Content: "hello",
Tp: model.TimePoint{
StartTime: time.Now().Unix(),
EndTime: time.Now().Unix() + 1000,
},
}
//插入某一条数据
if iResult, err = collection.InsertOne(context.TODO(), lr); err != nil {
fmt.Print(err)
return
}
//_id:默认生成的一个全局唯一ID
id = iResult.InsertedID.(primitive.ObjectID)
fmt.Println("自增ID", id.Hex())
}
检查数据:
> use my_db
// 这里的查询格式是 db.集合.find()
> db.my_collection.find()
{ "_id" : ObjectId("62b7d1503b40d8d17476c2c4"), "jobName" : "PlanA", "command" : "echo 'say hello'", "err" : "", "content" : "hello", "tp" : { "startTime" : NumberLong(1656213840), "endTime" : NumberLong(1656214840) } }
批量插入数据
func main() {
var (
client = util.GetMgoCli()
err error
collection *mongo.Collection
result *mongo.InsertManyResult
id primitive.ObjectID
)
collection = client.Database("my_db").Collection("test")
//批量插入
result, err = collection.InsertMany(context.TODO(), []interface{}{
model.LogRecord{
JobName: "job10",
Command: "echo 1",
Err: "",
Content: "1",
Tp: model.TimePoint{
StartTime: time.Now().Unix(),
EndTime: time.Now().Unix() + 10,
},
},
model.LogRecord{
JobName: "job10",
Command: "echo 2",
Err: "",
Content: "2",
Tp: model.TimePoint{
StartTime: time.Now().Unix(),
EndTime: time.Now().Unix() + 10,
},
},
model.LogRecord{
JobName: "job10",
Command: "echo 3",
Err: "",
Content: "3",
Tp: model.TimePoint{
StartTime: time.Now().Unix(),
EndTime: time.Now().Unix() + 10,
},
},
model.LogRecord{
JobName: "job10",
Command: "echo 4",
Err: "",
Content: "4",
Tp: model.TimePoint{
StartTime: time.Now().Unix(),
EndTime: time.Now().Unix() + 10,
},
},
})
if err != nil {
log.Fatal(err)
}
if result == nil {
log.Fatal("result nil")
}
for _, v := range result.InsertedIDs {
id = v.(primitive.ObjectID)
fmt.Println("自增ID", id.Hex())
}
}
检查 MongoDB 的内容
> db.test.find()
{ "_id" : ObjectId("62b7d8d2fde90f10033b5076"), "jobName" : "job10", "command" : "echo 1", "err" : "", "content" : "1", "tp" : { "startTime" : NumberLong(1656215762), "endTime" : NumberLong(1656215772) } }
{ "_id" : ObjectId("62b7d8d2fde90f10033b5077"), "jobName" : "job10", "command" : "echo 2", "err" : "", "content" : "2", "tp" : { "startTime" : NumberLong(1656215762), "endTime" : NumberLong(1656215772) } }
{ "_id" : ObjectId("62b7d8d2fde90f10033b5078"), "jobName" : "job10", "command" : "echo 3", "err" : "", "content" : "3", "tp" : { "startTime" : NumberLong(1656215762), "endTime" : NumberLong(1656215772) } }
{ "_id" : ObjectId("62b7d8d2fde90f10033b5079"), "jobName" : "job10", "command" : "echo 4", "err" : "", "content" : "4", "tp" : { "startTime" : NumberLong(1656215762), "endTime" : NumberLong(1656215772) } }
查询数据
使用标准的查询方式需要先定义一个结构体用来查询,如下添加一个查询结构体:
//查询实体
type FindByJobName struct {
JobName string `bson:"jobName"` //任务名
}
之所以需要创建一个结构体用于查询是因为原本的那个 LogRecord 里面的其它值创建时默认是零值,会影响到查询(具体看下面的注释),整个查询代码如下所示:
func main() {
var (
client = util.GetMgoCli()
err error
collection *mongo.Collection
cursor *mongo.Cursor
)
//2.选择数据库 my_db里的某个表
collection = client.Database("my_db").Collection("test")
//如果直接使用 LogRecord{JobName: "job10"}是查不到数据的,因为其他字段有初始值0或者“”
cond := model.FindByJobName{JobName: "job10"}
//按照jobName字段进行过滤jobName="job10",翻页参数0-2
if cursor, err = collection.Find(context.TODO(), cond, options.Find().SetSkip(0), options.Find().SetLimit(2)); err != nil {
fmt.Println(err)
return
}
//延迟关闭游标
defer func() {
if err = cursor.Close(context.TODO()); err != nil {
log.Fatal(err)
}
}()
//遍历游标获取结果数据
for cursor.Next(context.TODO()) {
var lr model.LogRecord
//反序列化Bson到对象
if cursor.Decode(&lr) != nil {
fmt.Print(err)
return
}
//打印结果数据
fmt.Println(lr)
}
}
这里的结果遍历可以使用另外一种更方便的方式:
var results []model.LogRecord
if err = cursor.All(context.TODO(), &results); err != nil {
log.Fatal(err)
}
for _, result := range results {
fmt.Println(result)
}
使用 BSON 包去查询数据
前面查询数据时需要创建一个结构体来承载查询的字段,但是 MongoDB 这种 NoSQL 数据库最大的好处就是其是动态的,如果使用上面那种写法,那又和传统的关系型数据库有什么区别呢?所以应该可以使用 BSON 包提供的方法去查询
这一块的文档参考 Work with BSON
连接 MongoDB 的 Go 驱动程序中有两大类型表示 BSON 数据:D 和 Raw。
类型 D 家族被用来简洁地构建使用本地 Go 类型的 BSON 对象。这对于构造传递给 MongoDB 的命令特别有用。D 家族包括四类:
- D:一个 BSON 文档。这种类型应该在顺序重要的情况下使用,比如 MongoDB 命令。
- M:一张无序的 map。它和 D 是一样的,只是它不保持顺序。
- A:一个 BSON 数组。
- E:D 里面的一个元素
导入以下包就可以使用了:
import "go.mongodb.org/mongo-driver/bson"
使用例如下,用于查找 name 字段与 '张三' 或 '李四' 匹配的文档:
bson.D{{
"name",
bson.D{{
"$in",
bson.A{"张三", "李四"},
}},
}}
使用例:
func main() {
var (
client = util.GetMgoCli()
collection *mongo.Collection
err error
cursor *mongo.Cursor
)
//2.选择数据库 my_db里的某个表
collection = client.Database("my_db").Collection("test")
filter := bson.M{"jobName": "job10"}
if cursor, err = collection.Find(context.TODO(), filter, options.Find().SetSkip(0), options.Find().SetLimit(2)); err != nil {
log.Fatal(err)
}
//延迟关闭游标
defer func() {
if err = cursor.Close(context.TODO()); err != nil {
log.Fatal(err)
}
}()
//这里的结果遍历可以使用另外一种更方便的方式:
var results []model.LogRecord
if err = cursor.All(context.TODO(), &results); err != nil {
log.Fatal(err)
}
for _, result := range results {
fmt.Println(result)
}
}
聚合查询
func main() {
var (
client = util.GetMgoCli()
collection *mongo.Collection
err error
cursor *mongo.Cursor
)
//2.选择数据库 my_db里的某个表
collection = client.Database("my_db").Collection("test")
//filter := bson.M{"jobName": "job10"}
//按照 jobName 分组,countJob 中存储每组的数目
groupStage := mongo.Pipeline{{
{"$group", bson.D{
{"_id", "$jobName"},
{"countJob", bson.D{
{"$sum", 1},
}},
}},
}}
if cursor, err = collection.Aggregate(context.TODO(), groupStage); err != nil {
log.Fatal(err)
}
//延迟关闭游标
defer func() {
if err = cursor.Close(context.TODO()); err != nil {
log.Fatal(err)
}
}()
//遍历游标
var results []bson.M
if err = cursor.All(context.TODO(), &results); err != nil {
log.Fatal(err)
}
for _, result := range results {
fmt.Println(result)
}
}
打印输出:
$ go run .
map[_id:job10 countJob:4]
更新数据
同样的,使用 mongo-go-driver 进行更新也需要建立专门用于更新的实体
在这里建立的实体中存在 Command 和 Content 两个字段,更新时需要同时对这两个字段进行赋值,否则未被赋值的字段会被更新为 Golang 的数据类型初始值。
为更新方便可以采用 bson.M{"$set": bson.M{"command": "ByBsonM",}}
来进行更新
package model
// 更新实体
type UpdateByJobName struct {
Command string `bson:"command"` //shell命令
Content string `bson:"content"` //脚本输出
}
编写具体的代码
func main() {
var (
client = util.GetMgoCli()
collection *mongo.Collection
err error
uResult *mongo.UpdateResult
)
//2.选择数据库 my_db里的某个表
collection = client.Database("my_db").Collection("test")
filter := bson.M{"jobName": "job10"}
// update := bson.M{"$set": bson.M{"command": "ByBsonM",}}
update := bson.M{"$set": model.UpdateByJobName{Command: "byModel", Content: "model"}}
// update := bson.M{"$set": model.LogRecord{JobName:"job10",Command:"byModel"}}
if uResult, err = collection.UpdateMany(context.TODO(), filter, update); err != nil {
log.Fatal(err)
}
//uResult.MatchedCount表示符合过滤条件的记录数,即更新了多少条数据。
log.Println(uResult.MatchedCount)
}
这个 $set
表示修改字段的值,例如:
bson.M{"$set": model.UpdateByJobName{Command: "byModel", Content:"model"}}
使用 $inc
可以对字段的值进行增减计算,例如下面表示对 age 的值减一。
bson.M{"$inc": bson.M{ "age": -1, }}
使用 $push
可以对该字段增加一个元素,例如下面表示对 interests 字段的元素数组增加 Golang 元素。
bson.M{"$push": bson.M{ "interests": "Golang", }}
使用 $push
也可以对该字段删除一个元素,例如下面也表示对 interests 字段的元素数组删除 Golang 元素。
bson.M{"$pull": bson.M{ "interests": "Golang", }}
删除数据
func main() {
var (
client = util.GetMgoCli()
collection *mongo.Collection
err error
uResult *mongo.DeleteResult
//upsertedID model.LogRecord
)
//2.选择数据库 my_db里的某个表
collection = client.Database("my_db").Collection("test")
filter := bson.M{"jobName": "job0"}
//3.删除开始时间早于当前时间的数据
//
if uResult, err = collection.DeleteMany(context.TODO(), filter); err != nil {
log.Fatal(err)
}
log.Println(uResult.DeletedCount)
}
带过滤条件删除:
type DeleteCond struct {
BeforeCond TimeBeforeCond `bson:"tp.startTime"`
}
//startTime小于某时间,使用这种方式可以对想要进行的操作($set、$group等)提前定义
type TimeBeforeCond struct {
BeforeTime int64 `bson:"$lt"`
}
func main() {
var (
client = util.GetMgoCli()
collection *mongo.Collection
err error
uResult *mongo.DeleteResult
delCond *DeleteCond
//upsertedID model.LogRecord
)
//2.选择数据库 my_db里的某个表
collection = client.Database("my_db").Collection("test")
//3.删除jobName为job0的数据
delCond = &DeleteCond{BeforeCond: TimeBeforeCond{BeforeTime: time.Now().Unix()}}
if uResult, err = collection.DeleteMany(context.TODO(), delCond); err != nil {
log.Fatal(err)
}
log.Println(uResult.DeletedCount)
}